import { resLoader, CompletedCallback, ProcessCallback } from "./ResLoader";
import ResKeeper from "./ResKeeper";

//#region 
function parseDepends(key,parsed:Set<string>){
    let loader:any=cc.loader;
    var item=loader.getItem(key);
    if(item){
        var depends=item.dependKeys;
        if(depends){
            for(var i=0;i<depends.length;i++){
                var depend=depends[i];
                if(!parsed.has(depend)){
                    parsed.add(depend);
                    parseDepends(depend,parsed);
                }
            }
        }
    }
}

function visitAsset(asset,excludeMap:Set<string>){
    if(!asset._uuid)return;
    let loader:any=cc.loader;
    var key=loader._getReferenceKey(asset);
    if(!excludeMap.has(key)){
        excludeMap.add(key);
        parseDepends(key,excludeMap);
    }
}

function visitComponent(comp,excludeMap){
    var props=Object.getOwnPropertyNames(comp);
    for(var i=0;i<props.length;i++){
        var value=comp[props[i]];
        if(typeof value==='object'&&value){
            if(Array.isArray(value)){
                for(let j=0;i<value.length;j++){
                    let val=value[j];
                    if(val instanceof cc.RawAsset){
                        visitAsset(val,excludeMap);
                    }
                }
            }
            else if(!value.constructor||value.constructor===Object){
                let keys=Object.getOwnPropertyNames(value);
                for(let j=0;j<keys.length;j++) {
                    let val=value[keys[j]];
                    if(val instanceof cc.RawAsset){
                        visitAsset(val,excludeMap);
                    }
                }
            }
            else if(value instanceof cc.RawAsset){
                visitAsset(value,excludeMap);
            }
        }
    }
}

function visitNode(node,excludeMap){
    for(let i=0;i<node._components.length;i++){
        visitComponent(node._components[i],excludeMap);
    }
    for(let i=0;i<node._children.length;i++){
        visitNode(node._children[i],excludeMap);
    }
}
//#endregion

export default class ResUtil {
    public static loadRes(attachNode: cc.Node, url: string);
    public static loadRes(attachNode: cc.Node, url: string, onCompleted: CompletedCallback);
    public static loadRes(attachNode: cc.Node, url: string, onProgess: ProcessCallback, onCompleted: CompletedCallback);
    public static loadRes(attachNode: cc.Node, url: string, type: typeof cc.Asset);
    public static loadRes(attachNode: cc.Node, url: string, type: typeof cc.Asset, onCompleted: CompletedCallback);
    public static loadRes(attachNode: cc.Node, url: string, type: typeof cc.Asset, onProgess: ProcessCallback, onCompleted: CompletedCallback);
    
    public static loadRes() {
        let attachNode = arguments[0];
        let keeper = ResUtil.getResKeeper(attachNode);
        let newArgs = new Array();
        for(let i = 1; i < arguments.length; ++i) {
            newArgs[i - 1] = arguments[i];
        }
        keeper.loadRes.apply(keeper, newArgs);
    }
    
    public static getResKeeper(attachNode: cc.Node, autoCreate?: boolean): ResKeeper {
        if (attachNode) {
            let ret = attachNode.getComponent(ResKeeper);
            if (!ret) {
                if (autoCreate) {
                    return attachNode.addComponent(ResKeeper);
                } else {
                    return ResUtil.getResKeeper(attachNode.parent, autoCreate);
                }
            }
            return ret;
        }
        return resLoader.getResKeeper();
    }
    public static assignWith(srcAsset: cc.Asset, targetNode: cc.Node, autoCreate?: boolean): any {
        let keeper = ResUtil.getResKeeper(targetNode, autoCreate);
        if (keeper && srcAsset) {
            let url = resLoader.getResKeyByAsset(srcAsset);
            if (url) {
                keeper.autoReleaseRes({ url, use: resLoader.nextUseKey() });
                return srcAsset;
            }
        }
        console.error(`AssignWith ${srcAsset} to ${targetNode} faile`);
        return null;
    }
    public static instantiate(prefab: cc.Prefab): cc.Node {
        let node = cc.instantiate(prefab);
        let keeper = ResUtil.getResKeeper(node, true);
        if (keeper) {
            let url = resLoader.getResKeyByAsset(prefab);
            console.log(url);
            if (url) {
                keeper.autoReleaseRes({ url, type: cc.Prefab, use: resLoader.nextUseKey() });
                return node;
            }
        }
        console.warn(`instantiate ${prefab}, autoRelease faile`);
        return node;
    }
    public static getNodesDepends(nodes: cc.Node[]): Set<string> {
        let ret: Set<string> = new Set<string>();
        for (let i = 0; i < nodes.length; i++) {
            visitNode(nodes[i], ret)
        }
        return ret;
    }
}
